﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using ERTesting.Model.Config;
using ERTesting.Model.Result;

namespace ERTesting.Dao.XmlFileDao
{
    internal class XmlTestResultService : ITestResultService
    {
        #region Implementation of ITestResultService

        /// <summary>
        /// Returns all the result sets for the scenario from previous runs. 
        /// </summary>
        /// <param name="scenario">The scenario for which to get the results for.</param>
        /// <param name="callback">Called with the results sets for the scenario. null if an error occured.</param>
        void ITestResultService.GetAllResults(Scenario scenario, Action<IEnumerable<ScenarioResult>, Exception> callback)
        {
            try
            {
                string fileName = GetSummaryFileName(scenario);
                if (!new FileInfo(fileName).Exists)
                {
                    callback(Enumerable.Empty<ScenarioResult>(), null);
                }
                else
                {
                    XElement xElement = XElement.Load(fileName);
                    IEnumerable<ScenarioResult> results = from r in xElement.Elements("result")
                                                          select
                                                              new ScenarioResult((string) r.Attribute("id"),
                                                                                 (DateTime) r.Attribute("timestamp"),
                                                                                 (double) r.Attribute("runtime"),
                                                                                 (bool) r.Attribute("error"),
                                                                                 (bool) r.Attribute("pass"),
                                                                                 (string) r.Attribute("baseline"), scenario);
                    callback(results, null);
                }
            }
            catch (FileNotFoundException e)
            {
                callback(Enumerable.Empty<ScenarioResult>(), null);
            }
        }

        /// <summary>
        /// Saves the result set to the result store.
        /// </summary>
        /// <param name="scenarioResult">The result set to save.</param>
        /// <param name="callback">Called with the result of the operation.</param>
        void ITestResultService.SaveResult(ScenarioResult scenarioResult, Action<ScenarioResult, Exception> callback)
        {
            string summaryFileName = GetSummaryFileName(scenarioResult.Scenario);
            var fileInfo = new FileInfo(summaryFileName);
            if (!fileInfo.Exists)
            {
                CreateResultSummary(scenarioResult.Scenario, summaryFileName);
            }
            try
            {
                XElement document = XElement.Load(summaryFileName);


                document.Add(new XElement("result", new XAttribute("id", scenarioResult.Id),
                                          new XAttribute("timestamp", scenarioResult.TimeStamp),
                                          new XAttribute("runtime", scenarioResult.RunTime),
                                          new XAttribute("error", scenarioResult.IsError),
                                          new XAttribute("pass", scenarioResult.IsPass),
                                          new XAttribute("baseline", scenarioResult.BaselineId)));
                document.Save(summaryFileName);

                var results =
                    new XDocument(new XElement("result", new XAttribute("id", scenarioResult.Id),
                                               from o in scenarioResult.Outputs
                                               select
                                                   new XElement("output", new XAttribute("range", o.Range),
                                                                new XAttribute("actualvalue", o.ActualValue),
                                                                new XAttribute("signoff", o.SignOff.ToString()))));

                results.Save(GetResultFileName(scenarioResult.Scenario, scenarioResult));
                callback(scenarioResult, null);
            }
            catch (FileNotFoundException ex)
            {
                callback(null, new ApplicationException("Could not load result summary file for scenario."));
            }
        }

        /// <summary>
        /// Returns a new unique id for the result set.
        /// </summary>
        /// <returns>A unique ID string.</returns>
        string ITestResultService.GetNewResultId()
        {
            return Guid.NewGuid().ToString();
        }

        void ITestResultService.GetOutputsForResult(ScenarioResult scenarioResult, Action<IEnumerable<TestOutput>, Exception> callback)
        {
            string resultFileName = GetResultFileName(scenarioResult.Scenario, scenarioResult);
            try
            {
                IEnumerable<TestOutput> testOutputs = from o in XElement.Load(resultFileName).Elements("output") select new TestOutput((string)o.Attribute("range"), (string) o.Attribute("actualvalue")){SignOff = (SignOffEnum) Enum.Parse(typeof(SignOffEnum),(string)o.Attribute("signoff"))};
                
                callback(testOutputs, null);
            }
            catch(FileNotFoundException ex)
            {
                callback(null, new ApplicationException("Could not load the outputs for scenario result,", ex));
            }
        }

        public void GetDefaultBaselineResult(ScenarioResult scenarioResult, Action<ScenarioResult, Exception> callback)
        {
             string fileName = GetSummaryFileName(scenarioResult.Scenario);
             if (!new FileInfo(fileName).Exists)
             {
                 callback(null, new ApplicationException("There are no results for this scenario."));
             }
             else
             {
                 XElement xElement = XElement.Load(fileName);

                 if (String.IsNullOrEmpty(scenarioResult.BaselineId))
                 {

                     IEnumerable<ScenarioResult> results = (from r in xElement.Elements("result")
                                                            where !(bool) r.Attribute("error")
                                                            select
                                                                new ScenarioResult((string) r.Attribute("id"),
                                                                                   (DateTime) r.Attribute("timestamp"),
                                                                                   (double) r.Attribute("runtime"),
                                                                                   (bool) r.Attribute("error"),
                                                                                   (bool) r.Attribute("pass"),
                                                                                   (string) r.Attribute("baseline"),
                                                                                   scenarioResult.Scenario));
                     
                         ScenarioResult result = null;
                     if(results.Any())
                     {
                         IOrderedEnumerable<ScenarioResult> ordered = results.OrderByDescending(t => t.TimeStamp);
                         if(ordered.Any(r=>r.IsPass))
                         {
                             result = ordered.First(r => r.IsPass);
                         } else
                         {
                             result = ordered.First();
                         }
                     
                     }

                         callback(result, null);
                     

                     
                 }
                 else
                 {
                     IEnumerable<ScenarioResult> results = (from r in xElement.Elements("result")
                                                            where (string)r.Attribute("id") == scenarioResult.BaselineId
                                                            select
                                                                new ScenarioResult((string)r.Attribute("id"),
                                                                                   (DateTime)r.Attribute("timestamp"),
                                                                                   (double)r.Attribute("runtime"),
                                                                                   (bool)r.Attribute("error"),
                                                                                   (bool)r.Attribute("pass"),
                                                                                   (string)r.Attribute("baseline"),
                                                                                   scenarioResult.Scenario));
                     callback(results.First(), null);
                 }
             } 
        }

        #endregion

        #region Private Methods

        private string GetSummaryFileName(Scenario scenario)
        {
            return "./Results/" + scenario.ScenarioId + "/summary.xml";
        }

        private void CreateResultSummary(Scenario scenario, string summaryFileName)
        {
            Directory.CreateDirectory(new FileInfo(summaryFileName).DirectoryName);
            new XDocument(new XElement("results", new XAttribute("id", scenario.ScenarioId))).Save(summaryFileName);
        }

        private string GetResultFileName(Scenario scenario, ScenarioResult scenarioResult)
        {
            return "./Results/" + scenario.ScenarioId + "/" + scenarioResult.Id + ".xml";
        }

        #endregion Private Methods

    }
}